<template>
  <div class="file-uploader">
    <el-card class="upload-card">
      <template #header>
        <div class="card-header">
          <span>文件上传</span>
        </div>
      </template>

      <!-- 文件选择区域 -->
      <div class="upload-area" @click="selectFile" @drop="handleDrop" @dragover.prevent @dragenter.prevent>
        <el-icon class="upload-icon" size="48">
          <UploadFilled />
        </el-icon>
        <div class="upload-text">
          <p>点击选择文件或拖拽文件到这里</p>
          <p class="upload-hint">支持大文件上传，断点续传</p>
        </div>
        <input
          ref="fileInput"
          type="file"
          style="display: none"
          @change="handleFileSelect"
        />
      </div>

      <!-- 文件信息 -->
      <div v-if="currentFile" class="file-info">
        <el-descriptions title="文件信息" :column="2" border>
          <el-descriptions-item label="文件名">{{ currentFile.name }}</el-descriptions-item>
          <el-descriptions-item label="文件大小">{{ formatFileSize(currentFile.size) }}</el-descriptions-item>
          <el-descriptions-item label="文件类型">{{ currentFile.type || '未知' }}</el-descriptions-item>
          <el-descriptions-item label="MD5值">{{ fileMD5 || '计算中...' }}</el-descriptions-item>
        </el-descriptions>
      </div>

      <!-- MD5计算进度 -->
      <div v-if="isCalculatingMD5" class="md5-progress">
        <el-progress
          :percentage="md5Progress"
          :format="() => `MD5计算中: ${md5Progress}%`"
          status="success"
        />
      </div>

      <!-- 上传控制按钮 -->
      <div v-if="currentFile && fileMD5" class="upload-controls">
        <el-button
          v-if="uploadStatus === 'waiting'"
          type="primary"
          @click="startUpload"
          :loading="isUploading"
        >
          开始上传
        </el-button>
        
        <el-button
          v-if="uploadStatus === 'uploading'"
          type="warning"
          @click="pauseUpload"
        >
          暂停上传
        </el-button>
        
        <el-button
          v-if="uploadStatus === 'paused'"
          type="primary"
          @click="resumeUpload"
        >
          恢复上传
        </el-button>
        
        <el-button
          v-if="uploadStatus === 'uploading' || uploadStatus === 'paused'"
          type="danger"
          @click="cancelUpload"
        >
          取消上传
        </el-button>

        <el-button @click="resetUpload">重新选择文件</el-button>
      </div>

      <!-- 上传进度 -->
      <div v-if="showProgress" class="upload-progress">
        <el-progress
          :percentage="uploadProgress"
          :format="() => `${uploadProgress}% (${uploadedChunks}/${totalChunks})`"
          :status="getProgressStatus()"
        />
        <div class="progress-details">
          <span>上传速度: {{ uploadSpeed }}</span>
          <span>剩余时间: {{ remainingTime }}</span>
        </div>
      </div>

      <!-- 状态提示 -->
      <div v-if="statusMessage" class="status-message">
        <el-alert
          :title="statusMessage"
          :type="getAlertType()"
          :closable="false"
          show-icon
        />
      </div>

      <!-- 分片详情 -->
      <div v-if="showChunkDetails && chunks.length > 0" class="chunk-details">
        <el-collapse>
          <el-collapse-item title="分片上传详情" name="chunks">
            <div class="chunk-grid">
              <div
                v-for="(chunk, index) in chunks"
                :key="index"
                :class="getChunkClass(index)"
                class="chunk-item"
                :title="`分片 ${index}: ${getChunkStatus(index)}`"
              >
                {{ index }}
              </div>
            </div>
          </el-collapse-item>
        </el-collapse>
      </div>
    </el-card>
  </div>
</template>

<script>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue'
import {
  calculateFileMD5,
  createFileChunks,
  checkFileExists,
  getUploadedChunks,
  uploadChunk,
  mergeChunks,
  saveUploadState,
  getUploadState,
  clearUploadState,
  ConcurrencyController
} from '../utils/upload.js'

export default {
  name: 'FileUploader',
  components: {
    UploadFilled
  },
  setup() {
    // 响应式数据
    const fileInput = ref(null)
    const currentFile = ref(null)
    const fileMD5 = ref('')
    const isCalculatingMD5 = ref(false)
    const md5Progress = ref(0)
    const isUploading = ref(false)
    const uploadStatus = ref('waiting') // waiting, uploading, paused, completed, failed
    const uploadProgress = ref(0)
    const uploadedChunks = ref(0)
    const totalChunks = ref(0)
    const statusMessage = ref('')
    const showChunkDetails = ref(false)
    const chunks = ref([])
    const uploadedChunkIndexes = ref([])
    const retryCount = ref(0)
    const maxRetries = 3
    const startTime = ref(0)
    const uploadedBytes = ref(0)

    // 并发控制器
    let concurrencyController = new ConcurrencyController(3)
    let uploadAbortController = null

    // 计算属性
    const showProgress = computed(() => {
      return uploadStatus.value === 'uploading' || 
             uploadStatus.value === 'paused' || 
             uploadStatus.value === 'completed'
    })

    const uploadSpeed = computed(() => {
      if (!startTime.value || uploadedBytes.value === 0) return '0 KB/s'
      const elapsed = (Date.now() - startTime.value) / 1000
      const speed = uploadedBytes.value / elapsed
      return formatFileSize(speed) + '/s'
    })

    const remainingTime = computed(() => {
      if (!startTime.value || uploadedBytes.value === 0) return '计算中...'
      const elapsed = (Date.now() - startTime.value) / 1000
      const speed = uploadedBytes.value / elapsed
      const remaining = (currentFile.value.size - uploadedBytes.value) / speed
      if (remaining < 60) return `${Math.round(remaining)}秒`
      if (remaining < 3600) return `${Math.round(remaining / 60)}分钟`
      return `${Math.round(remaining / 3600)}小时`
    })

    // 方法
    const selectFile = () => {
      // 清空文件输入框的值，确保选择相同文件时也能触发change事件
      if (fileInput.value) {
        fileInput.value.value = ''
      }
      fileInput.value.click()
    }

    const handleFileSelect = (event) => {
      const file = event.target.files[0]
      if (file) {
        handleFile(file)
      }
    }

    const handleDrop = (event) => {
      event.preventDefault()
      const files = event.dataTransfer.files
      if (files.length > 0) {
        handleFile(files[0])
      }
    }

    const handleFile = async (file) => {
      currentFile.value = file
      fileMD5.value = ''
      resetUploadState()
      
      // 检查是否有保存的上传状态
      const savedState = getUploadState(file.name + file.size)
      if (savedState) {
        const shouldResume = await ElMessageBox.confirm(
          '检测到未完成的上传，是否继续上传？',
          '提示',
          {
            confirmButtonText: '继续上传',
            cancelButtonText: '重新上传',
            type: 'info'
          }
        ).catch(() => false)

        if (shouldResume) {
          fileMD5.value = savedState.md5
          uploadedChunkIndexes.value = savedState.uploadedChunks || []
          
          // 重新创建chunks（不能从localStorage恢复Blob对象）
          chunks.value = createFileChunks(file)
          totalChunks.value = chunks.value.length
          
          uploadProgress.value = Math.round((uploadedChunkIndexes.value.length / totalChunks.value) * 100)
          uploadedChunks.value = uploadedChunkIndexes.value.length
          uploadStatus.value = 'paused'
          statusMessage.value = '从断点继续上传'
          showChunkDetails.value = true
          return
        } else {
          clearUploadState(file.name + file.size)
        }
      }

      // 计算文件MD5
      await calculateMD5()
    }

    const calculateMD5 = async () => {
      isCalculatingMD5.value = true
      md5Progress.value = 0
      statusMessage.value = '正在计算文件MD5...'

      try {
        fileMD5.value = await calculateFileMD5(currentFile.value, (progress) => {
          md5Progress.value = progress
        })
        
        isCalculatingMD5.value = false
        statusMessage.value = 'MD5计算完成'
        
        // 检查文件是否存在（秒传）
        await checkInstantUpload()
      } catch (error) {
        isCalculatingMD5.value = false
        statusMessage.value = 'MD5计算失败: ' + error.message
        ElMessage.error('MD5计算失败')
      }
    }

    const checkInstantUpload = async () => {
      try {
        statusMessage.value = '检查文件是否已存在...'
        const checkResult = await checkFileExists(fileMD5.value, currentFile.value.name, currentFile.value.size)
        
        if (checkResult.data.exists) {
          uploadStatus.value = 'completed'
          uploadProgress.value = 100
          statusMessage.value = '文件已存在，秒传成功！'
          ElMessage.success('秒传成功！')
          clearUploadState(currentFile.value.name + currentFile.value.size)
          return
        }

        // 创建分片
        chunks.value = createFileChunks(currentFile.value)
        totalChunks.value = chunks.value.length
        
        // 获取已上传的分片
        if (checkResult.data.uploadedChunks && checkResult.data.uploadedChunks.length > 0) {
          uploadedChunkIndexes.value = checkResult.data.uploadedChunks
          uploadedChunks.value = uploadedChunkIndexes.value.length
          uploadProgress.value = Math.round((uploadedChunks.value / totalChunks.value) * 100)
          statusMessage.value = `发现${uploadedChunks.value}个已上传分片，可从断点继续`
          showChunkDetails.value = true
        } else {
          statusMessage.value = '准备上传'
        }
        
        uploadStatus.value = 'waiting'
      } catch (error) {
        statusMessage.value = '检查文件失败: ' + error.message
        ElMessage.error('检查文件失败')
      }
    }

    const startUpload = async () => {
      if (!currentFile.value || !fileMD5.value) return
      
      uploadStatus.value = 'uploading'
      isUploading.value = true
      startTime.value = Date.now()
      uploadAbortController = new AbortController()
      showChunkDetails.value = true
      
      // 保存上传状态（不保存chunks，因为Blob对象无法序列化）
      saveUploadState(currentFile.value.name + currentFile.value.size, {
        md5: fileMD5.value,
        totalChunks: totalChunks.value,
        uploadedChunks: uploadedChunkIndexes.value
      })

      try {
        await uploadChunks()
      } catch (error) {
        if (error.name !== 'AbortError') {
          handleUploadError(error)
        }
      }
    }

    const uploadChunks = async () => {
      // 找出未上传的分片
      const pendingChunks = chunks.value.filter(chunk => 
        !uploadedChunkIndexes.value.includes(chunk.index)
      )

      statusMessage.value = `开始上传，共${pendingChunks.length}个分片待上传`

      // 使用并发控制器上传分片
      const uploadPromises = pendingChunks.map(chunkData => 
        concurrencyController.add(async () => {
          if (uploadAbortController.signal.aborted) {
            throw new Error('Upload aborted')
          }

          let attempts = 0
          while (attempts < maxRetries) {
            try {
              await uploadChunk(
                fileMD5.value,
                chunkData.index,
                totalChunks.value,
                chunkData.chunk,
                (progress) => {
                  // 单个分片的进度回调可以在这里处理
                }
              )
              
              // 更新已上传分片
              uploadedChunkIndexes.value.push(chunkData.index)
              uploadedChunks.value = uploadedChunkIndexes.value.length
              uploadProgress.value = Math.round((uploadedChunks.value / totalChunks.value) * 100)
              uploadedBytes.value += chunkData.size
              
              // 保存状态（不保存chunks，因为Blob对象无法序列化）
              saveUploadState(currentFile.value.name + currentFile.value.size, {
                md5: fileMD5.value,
                totalChunks: totalChunks.value,
                uploadedChunks: uploadedChunkIndexes.value
              })
              
              break
            } catch (error) {
              attempts++
              if (attempts >= maxRetries) {
                throw error
              }
              await new Promise(resolve => setTimeout(resolve, 1000 * attempts))
            }
          }
        })
      )

      await Promise.all(uploadPromises)

      if (uploadAbortController.signal.aborted) {
        return
      }

      // 所有分片上传完成，合并文件
      await mergeFile()
    }

    const mergeFile = async () => {
      try {
        statusMessage.value = '正在合并文件...'
        const result = await mergeChunks(
          fileMD5.value,
          currentFile.value.name,
          totalChunks.value,
          currentFile.value.size
        )

        if (result.success) {
          uploadStatus.value = 'completed'
          uploadProgress.value = 100
          statusMessage.value = '文件上传成功！'
          ElMessage.success('文件上传成功！')
          clearUploadState(currentFile.value.name + currentFile.value.size)
        } else {
          throw new Error(result.message)
        }
      } catch (error) {
        handleUploadError(error)
      } finally {
        isUploading.value = false
      }
    }

    const pauseUpload = () => {
      if (uploadAbortController) {
        uploadAbortController.abort()
      }
      uploadStatus.value = 'paused'
      isUploading.value = false
      statusMessage.value = '上传已暂停'
    }

    const resumeUpload = () => {
      startUpload()
    }

    const cancelUpload = async () => {
      try {
        await ElMessageBox.confirm('确定要取消上传吗？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })

        if (uploadAbortController) {
          uploadAbortController.abort()
        }
        
        resetUploadState()
        clearUploadState(currentFile.value.name + currentFile.value.size)
        statusMessage.value = '上传已取消'
      } catch {
        // 用户取消了确认对话框
      }
    }

    const resetUpload = () => {
      currentFile.value = null
      resetUploadState()
    }

    const resetUploadState = () => {
      fileMD5.value = ''
      isCalculatingMD5.value = false
      md5Progress.value = 0
      isUploading.value = false
      uploadStatus.value = 'waiting'
      uploadProgress.value = 0
      uploadedChunks.value = 0
      totalChunks.value = 0
      statusMessage.value = ''
      showChunkDetails.value = false
      chunks.value = []
      uploadedChunkIndexes.value = []
      retryCount.value = 0
      startTime.value = 0
      uploadedBytes.value = 0
      
      if (uploadAbortController) {
        uploadAbortController.abort()
        uploadAbortController = null
      }
    }

    const handleUploadError = (error) => {
      uploadStatus.value = 'failed'
      isUploading.value = false
      statusMessage.value = '上传失败: ' + error.message
      ElMessage.error('上传失败: ' + error.message)
    }

    const formatFileSize = (bytes) => {
      if (bytes === 0) return '0 B'
      const k = 1024
      const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
    }

    const getProgressStatus = () => {
      switch (uploadStatus.value) {
        case 'completed': return 'success'
        case 'failed': return 'exception'
        case 'paused': return 'warning'
        default: return ''
      }
    }

    const getAlertType = () => {
      if (statusMessage.value.includes('成功') || statusMessage.value.includes('完成')) return 'success'
      if (statusMessage.value.includes('失败') || statusMessage.value.includes('错误')) return 'error'
      if (statusMessage.value.includes('暂停')) return 'warning'
      return 'info'
    }

    const getChunkClass = (index) => {
      if (uploadedChunkIndexes.value.includes(index)) {
        return 'chunk-uploaded'
      }
      return 'chunk-pending'
    }

    const getChunkStatus = (index) => {
      if (uploadedChunkIndexes.value.includes(index)) {
        return '已上传'
      }
      return '待上传'
    }

    return {
      fileInput,
      currentFile,
      fileMD5,
      isCalculatingMD5,
      md5Progress,
      isUploading,
      uploadStatus,
      uploadProgress,
      uploadedChunks,
      totalChunks,
      statusMessage,
      showChunkDetails,
      chunks,
      uploadedChunkIndexes,
      showProgress,
      uploadSpeed,
      remainingTime,
      selectFile,
      handleFileSelect,
      handleDrop,
      startUpload,
      pauseUpload,
      resumeUpload,
      cancelUpload,
      resetUpload,
      formatFileSize,
      getProgressStatus,
      getAlertType,
      getChunkClass,
      getChunkStatus
    }
  }
}
</script>

<style scoped>
.file-uploader {
  max-width: 800px;
  margin: 0 auto;
}

.upload-card {
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 600;
}

.upload-area {
  border: 2px dashed #d9d9d9;
  border-radius: 6px;
  padding: 60px 20px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.3s;
  margin-bottom: 20px;
}

.upload-area:hover {
  border-color: #409EFF;
}

.upload-icon {
  color: #c0c4cc;
  margin-bottom: 16px;
}

.upload-text p {
  margin: 8px 0;
  color: #606266;
}

.upload-hint {
  font-size: 12px;
  color: #c0c4cc;
}

.file-info {
  margin: 20px 0;
}

.md5-progress {
  margin: 20px 0;
}

.upload-controls {
  display: flex;
  gap: 10px;
  margin: 20px 0;
  flex-wrap: wrap;
}

.upload-progress {
  margin: 20px 0;
}

.progress-details {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 12px;
  color: #909399;
}

.status-message {
  margin: 20px 0;
}

.chunk-details {
  margin: 20px 0;
}

.chunk-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 4px;
  max-height: 200px;
  overflow-y: auto;
}

.chunk-item {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  font-size: 12px;
  cursor: default;
}

.chunk-uploaded {
  background-color: #67c23a;
  color: white;
}

.chunk-pending {
  background-color: #e4e7ed;
  color: #606266;
}

@media (max-width: 768px) {
  .upload-controls {
    flex-direction: column;
  }
  
  .progress-details {
    flex-direction: column;
    gap: 4px;
  }
  
  .chunk-grid {
    grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
  }
  
  .chunk-item {
    width: 30px;
    height: 30px;
    font-size: 10px;
  }
}
</style> 